package edu.office.applicationRepository;

import edu.office.entities.*;
import edu.office.tools.StaticFields;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import org.jgrapht.Graph;
import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.graph.DirectedMultigraph;

import java.util.*;

@Named("subjectRepositoryA")
@ApplicationScoped
public class SubjectRepository extends ApplicationCommonRepository<Subject> {

    private final String tableName = "subject";
    HashMap<Integer, Subject> subjectMap = new HashMap<>();
    HashMap<Subject, List<Knowledge>> subjectSortedKnowledge;
    @Inject
    KnowledgeRepository knowledgeRepository;
    @Inject
    SchoolRepository schoolRepository;
    @Inject
    MajorRepository majorRepository;
    @Inject
    MajorsubjectRepository majorsubjectRepository;
    @Inject
    EdgeAmongKnowledgeRepository edgeamongknowledgeRepository;
    @Inject
    EdgeamongSubjectRepository edgeamongsubjectRepository;
    @Inject
    TeacschoolsubjectRepository teacschoolsubjectRepository;
    @Inject
    LeadpointRepository leadpointRepository;
    List<Subject> allSubject = new LinkedList<>();
    Comparator<Subject> subjectComparator = (Subject o1, Subject o2) -> {
        if (null == o1 || null == o2 || null == o1.getNAME() || null == o2.getNAME()) {
            return 0;
        } else {
            return o1.getNAME().compareToIgnoreCase(o2.getNAME());
        }
    };
    private HashMap<Subject, Graph> existedGraph = new HashMap<>();
    @Inject
    private SubjectRepository applicationSubjectController;
    @Inject
    private KnowledgeRepository applicationKnowledgeController;

    public String getKnowlegeIdsString(Subject subject) {
        List<Integer> ids = knowledgeRepository.getKnowledgeId4Subject(subject);
        if (!ids.isEmpty()) {
            return ids.toString().substring(1, ids.toString().length() - 1);
        } else {
            return "";
        }
    }

    public HashMap<Integer, Subject> getSubjectMap() {
        if (subjectMap.isEmpty() || isDataChanged()) {
            subjectMap.clear();
            List<Subject> all = getAllSubject();
            for (Subject subject : all) {
                subjectMap.put(subject.getID(), subject);
            }
        }
        return subjectMap;
    }

    public List<Subject> getAllSubject() {
        if (allSubject.isEmpty() || isDataChanged()) {
            allSubject = listAll();
        }
        return allSubject;
    }

    public Set<Subject> getSubject4School(School school) {
        //schoold-->major-->subject
        Major major = majorRepository.getIdMap().get(school.getMAJORID());
        return getSubject4Major(major);
    }

    public Set<Subject> getSubject4Major(Major major) {
        List<Majorsubject> majorsubjects = majorsubjectRepository.find("MAJORID", major.getID()).list();
        List<Integer> subjectIdInt = new LinkedList<>();
        for (Majorsubject majorsubject : majorsubjects) {
            subjectIdInt.add(majorsubject.getSUBJECTID());
        }
        if (!subjectIdInt.isEmpty()) {
            HashSet<Subject> result = new HashSet<>();
            getAllSubject().forEach(subject -> {
                if (subjectIdInt.contains(subject.getID())) {
                    result.add(subject);
                    subjectIdInt.remove(subject.getID());
                }
            });
            return result;
        } else return new HashSet<>();
    }

    public Set<Subject> getSubject4Student(Student student) {
        return getSubject4School(
                schoolRepository.getSchoolById(student.getSCHOOL_ID()));
    }

    public Set<Subject> getSubject4StudentFromLeadpoint(Student student) {
        String temSQL = null;
        Set<Subject> subjectSet = new HashSet<>();
        List<Leadpoint> leadpoints = leadpointRepository.list("student_id", student.getId());
        leadpoints.forEach((t) -> subjectSet.add(findById(t.getSUBJECTID())));
        return subjectSet;
    }

    public Subject findById(Integer id) {
        Subject result = null;
        for (Subject subject : getAllSubject())
            if (subject.getID().equals(id)) {
                result = subject;
                break;
            }
        return result;
    }

    public HashSet<Subject> getPredcessorSubjects(Subject subject) {
        HashSet<Subject> predcessorKnowledgeSet = new HashSet<>();
        Set<Edgeamongsubject> tem = new HashSet<>(edgeamongsubjectRepository.list("PREDECESSORNODE", subject.getID()));
        for (Edgeamongsubject edgeamongsubject : tem) { //拿到了一条记录，在该记录中，knowledge作后继，因此，可以找到它的前驱
            predcessorKnowledgeSet.add(find("ID", edgeamongsubject.getPREDECESSORNODE()).firstResult());
        }
        return predcessorKnowledgeSet;
    }

    public Set<Subject> getSubject4Teacher(TeacherAdmin teacher) {
        //teacher->teacherschoolsubject->subject
        List<Teacschoolsubject> tem = teacschoolsubjectRepository.find("TEACHERID", teacher.getID()).list();
        List<Integer> subjectIds = new LinkedList<>();
        for (Teacschoolsubject tss : tem) {
            subjectIds.add(tss.getSUBJECTID());
        }
        if (!subjectIds.isEmpty()) {
            HashSet<Subject> result = new HashSet();
            getAllSubject().forEach(subject -> {
                if (subjectIds.contains(subject.getID())) {
                    result.add(subject);
                    subjectIds.remove(subject.getID());
                }
            });
            return result;
        } else return new HashSet<>();
    }

    public Graph getGraphofKnowledge(Subject subject) {
        if (existedGraph.get(subject) == null) {
            Graph<Knowledge, DefaultEdge> directedGraph = new DirectedMultigraph<>(DefaultEdge.class);
            List<Knowledge> kl = knowledgeRepository.list("SUBJECT_ID", subject.getID());
            kl.forEach(directedGraph::addVertex);
            HashSet<Edgeamongknowledge> temSet1 = new HashSet<>();
            kl.stream().map((knowledge) -> {
                Set<Edgeamongknowledge> temSet =
                        new HashSet<>(edgeamongknowledgeRepository.getEdgeamongknowledges4KnowledgePreOrSucc(
                                knowledge.getID(), StaticFields.PREDCESSOR));
                for (Edgeamongknowledge edge : temSet) {
                    if (!temSet1.contains(edge)) {
                        directedGraph.addEdge(
                                knowledgeRepository.find("ID", edge.getPREDECESSORNODE()).firstResult(),
                                knowledgeRepository.find("ID", edge.getSUCCESSORNODE()).firstResult());
                        temSet1.add(edge);
                    }
                }
                temSet = new HashSet<>(edgeamongknowledgeRepository.getEdgeamongknowledges4KnowledgePreOrSucc(
                        knowledge.getID(), StaticFields.SUCCESSOR));
                return temSet;
            }).forEachOrdered((temSet) -> {
                for (Edgeamongknowledge edge : temSet) {
                    if (!temSet1.contains(edge)) {
                        directedGraph.addEdge(
                                knowledgeRepository.find("ID", edge.getPREDECESSORNODE()).firstResult(),
                                knowledgeRepository.find("ID", edge.getSUCCESSORNODE()).firstResult());
                    }
                }
            });
            existedGraph.put(subject, directedGraph);
        }
        return existedGraph.get(subject);
    }

    public void repaireKnowledgeRelation() {
        List<Subject> allSubject = getAllSubject();
        for (Subject subject : allSubject) {
            if (subject.getSTARTKNOWLEDGE() == null) {
                Knowledge knowledge;
                List<Knowledge> candiKnowledge = knowledgeRepository.find("select * from knowledge where subject_id=" + subject.getID() + " and ( name like '%start'or name like '%Start')").list();
                if (!candiKnowledge.isEmpty()) {
                    knowledge = candiKnowledge.get(0);
                } else {
                    knowledge = knowledgeRepository.createStart(subject);
                }
                subject.setSTARTKNOWLEDGE(knowledge.getID());
            }
        }
        knowledgeRepository.repaireKnwoledge();
        System.out.println("完成修复");
    }

    public List<Knowledge> getSortedKnowledges(Subject subject) {
        if (subjectSortedKnowledge.get(subject) == null) {
            knowledgeRepository.getSortedKnowledges(subject);
        }
        return subjectSortedKnowledge.get(subject);
    }

    public Set<Subject> getPredcessorSubject(Subject subject) {
        HashSet<Edgeamongsubject> preEdges = new HashSet<>(
                edgeamongsubjectRepository.getEdgeAmongSubject4SubjectPreOrSucc(subject.getID(), StaticFields.PREDCESSOR)
        );
        List<Integer> ids = new LinkedList<>();
        for (Edgeamongsubject es : preEdges) {
            ids.add(es.getPREDECESSORNODE());
        }

        if (!ids.isEmpty()) {
            Set<Subject> result = new HashSet<>();
            getAllSubject().forEach(subject1 -> {
                if (ids.contains(subject1.getID())) {
                    result.add(subject1);
                    ids.remove(subject1.getID());
                }
            });
            return result;
        }
        return new HashSet<>();
    }

    public Set<Subject> getSuccessorSubject(Subject subject) {
        HashSet<Edgeamongsubject> preEdges = new HashSet<>(
                edgeamongsubjectRepository.getEdgeAmongSubject4SubjectPreOrSucc(subject.getID(), StaticFields.SUCCESSOR)
        );
        List<Integer> ids = new LinkedList<>();
        for (Edgeamongsubject es : preEdges) {
            ids.add(es.getSUCCESSORNODE());
        }
        if (!ids.isEmpty()) {
            Set<Subject> result = new HashSet<>();
            getAllSubject().forEach(subject1 -> {
                if (ids.contains(subject1.getID())) {
                    result.add(subject1);
                    ids.remove(subject1.getID());
                }
            });
            return result;
        }
        return new HashSet<>();
    }

    public void moveResource(Subject from, Subject to) {
        List<Majorsubject> majorsubjects = majorsubjectRepository.list("SUBJECTID", from.getID());
        majorsubjects.forEach((majSub) -> {
            majSub.setSUBJECTID(to.getID());
            majorsubjectRepository.edit();
        });
        List<Teacschoolsubject> teacschoolsubjects = teacschoolsubjectRepository.list("SUBJECTID", from.getID());
        teacschoolsubjects.forEach((teacherschoolsubject) -> {
            teacherschoolsubject.setSUBJECTID(to.getID());
            teacschoolsubjectRepository.edit();
        });
        List<Leadpoint> leadpoints = leadpointRepository.list("STUDENT_ID", from.getID());
        leadpoints.forEach((leadpoint) -> {
            leadpoint.setSUBJECTID(to.getID());
            leadpointRepository.edit();
        });
        List<Edgeamongsubject> edgeamongsubjects = edgeamongsubjectRepository.list("SUCCESSORNODE", from.getID());
        edgeamongsubjects.forEach((edgeamongsubject) -> {
            if (edgeamongsubject.getPREDECESSORNODE().equals(from.getID())) {
                edgeamongsubject.setPREDECESSORNODE(to.getID());
                edgeamongsubjectRepository.edit();
            } else if (edgeamongsubject.getSUCCESSORNODE().equals(from.getID())) {
                edgeamongsubject.setSUCCESSORNODE(to.getID());
                edgeamongsubjectRepository.edit();
            }

        });
        edgeamongsubjects = edgeamongsubjectRepository.list("PREDECESSORNODE", from.getID());
        edgeamongsubjects.forEach((edgeamongsubject) -> {
            if (edgeamongsubject.getSUCCESSORNODE().equals(from.getID())) {
                edgeamongsubject.setSUCCESSORNODE(to.getID());
                edgeamongsubjectRepository.edit();
            } else if (edgeamongsubject.getPREDECESSORNODE().equals(from.getID())) {
                edgeamongsubject.setPREDECESSORNODE(to.getID());
                edgeamongsubjectRepository.edit();
            }
        });
        List<Knowledge> knowledges = knowledgeRepository.list("SUBJECT_ID", from.getID());
        knowledges.forEach((knowledge) -> {
            knowledge.setSUBJECT_ID(to.getID());
            knowledgeRepository.edit();
        });
    }

    public Set<Subject> getSubjectList4Testpaper(Testpaper testpaper) {

        HashSet<Subject> result = new HashSet<>();
        List<Integer> ids = new LinkedList<>();
        for (String id : testpaper.getSUBJECTIDS().split(",")) {
            ids.add(Integer.valueOf(id));
        }
        getAllSubject().forEach(subject -> {
            if (ids.contains(subject.getID())) {
                result.add(subject);
                ids.remove(subject.getID());
            }
        });
        return result;
    }

    public String myCreate(Subject subject) {
        //1. check whether the name existed? If true, deny the operation;else continue;
        if (find("NAME", subject.getNAME()).list().isEmpty()) {
            create(subject);
            applicationKnowledgeController.createStart(subject);
            return "Succeed";
        } else {
            return subject.getNAME() + ": Already Exist";
        }
    }

    public String myDestroy(Subject subjectFrom, Subject subjectTo) {
        moveResource(subjectFrom, subjectTo);
        destroy(subjectFrom);
        return "Succeed";
    }
}
